﻿using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using FDSimpleModelGenerator.Common;
using FDSimpleModelGenerator.Common.Enums;
using FDSimpleModelGenerator.Entities;
using FDSimpleModelGenerator.Entities.Enums;
using FDSimpleModelGenerator.IServices;

namespace FDSimpleModelGenerator.Services.SqlServer
{
    /// <summary>
    /// 生成SqlServer模型
    /// </summary>
    public class GenerateModelSqlServerService : IGenerateModelService
    {
        private readonly IExceptionThrowFactory _exceptionThrowFactory;
        private readonly IDbConfigResolver _configResolver;
        private readonly IDbAccessor _dbAccessor;
        private readonly IModelGenerateEngine _modelGenerateEngine;

        public GenerateModelSqlServerService(IExceptionThrowFactory exceptionThrowFactory, IDbAccessor dbAccessor,
            IModelGenerateEngine modelGenerateEngine, IEnumerable<IDbConfigResolver> dbConfigResolvers)
        {
            _dbAccessor = dbAccessor;
            _modelGenerateEngine = modelGenerateEngine;
            _exceptionThrowFactory = exceptionThrowFactory;
            _configResolver = dbConfigResolvers.First(r => r.DbType == DbType);
        }

        public DbType DbType => DbType.SqlServer;

        public async Task<string> GenerateAsync(DbConfig dbConfig, string tableName, string templateContent,
            string namespaceName = "FDSimpleModelGenerator", LanguageDataType languageDataType = LanguageDataType.CSharp, bool propertyFirstCharLower = false)
        {
            return await Task.Run(() =>
                Generate(dbConfig, tableName, templateContent, namespaceName, languageDataType, propertyFirstCharLower));
        }

        /// <summary>
        /// 生成模型
        /// </summary>
        /// <param name="dbConfig"></param>
        /// <param name="tableName"></param>
        /// <param name="templateContent"></param>
        /// <param name="namespaceName">命名空间</param>
        /// <param name="propertyFirstCharLower"></param>
        /// <param name="languageDataType"></param>
        /// <returns></returns>
        public string Generate(DbConfig dbConfig, string tableName, string templateContent, string namespaceName, LanguageDataType languageDataType, bool propertyFirstCharLower)
        {
            var connString = _configResolver.GetConnectionString(dbConfig);
            _dbAccessor.Init(() => new SqlConnection(connString));
            var sql =
                @"SELECT c.name,t.name AS ColumnType,ep.value Description,c.max_length AS Length,c.precision,[Key]=CONVERT(BIT,CASE WHEN EXISTS (SELECT TOP 1 1 FROM sys.objects WHERE type='PK' AND parent_object_id=o.object_id AND name IN (SELECT name FROM sys.sysindexes WHERE indid IN (SELECT indid FROM sys.sysindexkeys WHERE id=c.object_id AND colid=c.column_id))) THEN 1 ELSE 0 END) FROM sys.columns AS c INNER JOIN sys.types AS t ON  t.user_type_id=c.user_type_id
INNER JOIN sys.objects AS o ON o.object_id=c.object_id
LEFT JOIN sys.extended_properties ep ON ep.major_id=c.object_id AND ep.minor_id=c.column_id AND ep.class=1
 WHERE o.object_id=OBJECT_ID(@tableName)";

            var sqlTable = @" SELECT t.name, ep.value FROM sys.tables t LEFT JOIN sys.extended_properties ep ON ep.major_id=t.object_id AND t.type='U' AND ep.minor_id=0
 WHERE t.name=@tableName";
            var tableInfo = _dbAccessor.Get<NameValue<string>>(sqlTable, new { tableName });
            var columns = _dbAccessor.GetList<TableColumn>(sql, new { tableName });
            var tableParam = new TableInfo
            {
                Description = tableInfo.Value,
                Name = tableInfo.Name,
                TableColumns = columns
            };
            return _modelGenerateEngine.Generate(tableParam, templateContent, namespaceName, languageDataType, propertyFirstCharLower);
        }
    }
}
